package com.demo.config.filter;

import com.demo.common.base.R;
import com.demo.common.enums.REnum;
import com.demo.config.cache.ObjectCacheConfig;
import com.demo.config.token.OAuth2Token;
import com.demo.modules.redis.SysTokenRedis;
import com.demo.modules.sys.entity.SysUserTokenEntity;
import com.demo.modules.sys.service.SysUserTokenService;
import com.google.gson.Gson;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.time.LocalDateTime;

/**
 * oauth2过滤器
 *
 * @author Mark sunlightcs@gmail.com
 */
public class OAuth2Filter extends AuthenticatingFilter {

    private SysTokenRedis sysTokenRedis;
    private SysUserTokenService sysUserTokenService;

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        //获取请求token
        String token = getRequestToken((HttpServletRequest) request);
        if (StringUtils.isBlank(token)) {
            return null;
        }
        return new OAuth2Token(token);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse res = (HttpServletResponse) response;
        res.setHeader("Access-Control-Allow-Origin", "true");
        res.setHeader("Access-Control-Allow-Credentials", "true");
        res.setContentType("application/json; charset=utf-8");
        res.setStatus(HttpServletResponse.SC_OK);
        WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
        sysTokenRedis = applicationContext.getBean(SysTokenRedis.class);
        sysUserTokenService = applicationContext.getBean(SysUserTokenService.class);
        // 存在认证对象，放行
        if (this.getSubject(request, response).getPrincipal() != null) {
            //获取请求token
            String token = getRequestToken((HttpServletRequest) request);
            //获取cache 缓存
            SysUserTokenEntity sysUserTokenEntity = ObjectCacheConfig.tokenCache.get(token);
            if (sysUserTokenEntity == null) {
                //获取 Redis 缓存
                sysUserTokenEntity = sysTokenRedis.get(token);
            }
            if (sysUserTokenEntity != null && LocalDateTime.now().isAfter(sysUserTokenEntity.getExpireTime())) {
                PrintWriter writer = res.getWriter();
                R r = R.error(REnum.UN_LOGIN.getCode(), "Token超时，请重新登录");
                writer.write(new Gson().toJson(r));
                writer.close();
                return false;
            } else {
                if (sysUserTokenEntity == null) {
                    PrintWriter writer = res.getWriter();
                    R r = R.error(REnum.UN_LOGIN.getCode(), "Token无效");
                    writer.write(new Gson().toJson(r));
                    writer.close();
                    return false;
                } else {
                    sysUserTokenEntity = sysUserTokenService.createToken(sysUserTokenEntity.getId(), sysUserTokenEntity.getTokenType());
                    //缓存
                    ObjectCacheConfig.tokenCache.put(sysUserTokenEntity.getToken(), sysUserTokenEntity);
                    //redis 缓存
                    sysTokenRedis.saveOrUpdate(sysUserTokenEntity);
                }
            }
            return true;
        }
        PrintWriter writer = res.getWriter();
        R r = R.error(REnum.UN_LOGIN.getCode(), "未获取到用户认证信息");
        writer.write(new Gson().toJson(r));
        writer.close();
        //return false 拦截， true 放行
        return false;
    }

    /**
     * 获取请求的token
     */
    private String getRequestToken(HttpServletRequest httpRequest) {
        //从header中获取token
        String token = httpRequest.getHeader("token");

        //如果header中不存在token，则从参数中获取token
        if (StringUtils.isBlank(token)) {
            token = httpRequest.getParameter("token");
        }

        return token;
    }


}
